<?php 

/**
 * El paquete de un producto, constituye un conjunto de
 * servicios ofrecidos en una cantidad determinada,
 * a ser pagados, en conjunto, con una frecuencia determinada.
 *
 *@author Carlo Lopez <carlo.xavier.lopez 'en' gmail 'punto' com>
 */
class Paquete
{

  /**
   * conjunto de las reglas que delimitan los servicios
   * incluidos en el paquete, indexadas segun el nombre
   * del servicio al que se refieren.
   * P.e.: una regla para el servicio sms estaria indexada asi,
   *       $serv_reglas['sms'].
   */
  protected $serv_reglas = array();

  /**
   * Frecuencia con que se cobrara el paquete
   */
  protected $frecuencia = null; 

  /**
   * El producto que contiene el paquete 
   */
  protected $producto = null;

  protected $plan = null;
  
  /**
   * El precio del paquete.
   */
  protected $precio = 0;

  /**
   * indica si el paquete es nuevo o es un despacho antiguo.
   */
  protected $nuevo = true;

  /**
   * En caso de que el paquete ya se hubiera conformado para
   * ser entregado.
   */
  protected $entrega = null;

  /**
   * informacion necesaria para instalar el producto.
   */
  protected $installinfo;
  //TODO: se utiliza para establecer info de instalacion antes del pago

  
  protected $status;

  const NUEVO = 'NUEVO';

  const REPETIDO = 'REPETIDO';

  /**
   * Construye un nuevo paquete, desde el producto y las reglas 
   * suministrados.
   *
   * @param $nomprod  Producto  El nombre del producto que contendrá el paquete.
   * @param $regls Array<Regla> Los id de las reglas para consumo de servicios.
   * @param $frec  Frecuencia   El id de la frecuencia de pagos.
   */
  public function __construct($nomprod = null, $idregls=null, $idfrec=null, $status = self::NUEVO)
  {
    if(null != $nomprod) { 
        $this->setProducto(Doctrine_Core::getTable('Producto')->getProductByName($nomprod));
        $this->serv_reglas = array();
        $this->setReglas($idregls);
        $this->setFrecuencia(Doctrine_Core::getTable('Frecuencia')->find($idfrec));
        $this->status = $status;
    } 
  }

  /**
   * Se crea un paquete a partir de un plan y una frecuencia de pagos.
   * 
   * @param Plan   $plan  Plan de servicios 
   * @param String $idfrec  Frecuencia de pago 
   */
  public function fromPlan($plan, $idfrec, $status = self::NUEVO) {
      $reglas = $plan->getReglas();
      $reglasid = array();
      foreach($reglas as $regla)
          $reglasid[] = $regla->id;
      $prodname = $plan->getProductId();
      $producto = Doctrine_Core::getTable('Producto')->find($prodname);
      
      $this->setPlan($plan);
      $this->setProducto($producto);
      $this->setReglas($reglasid);
      $this->setFrecuencia(Doctrine_Core::getTable('Frecuencia')->find($idfrec));
      $this->status = $status;
  }
  
  public function setStatus($status)
  {
    $this->status = $status;
  }

  public function getStatus()
  {
    return $this->status;
  }

  /**
   * Agrega una regla a este paquete.
   *
   * @param $regla Regla  La regla que se quiere agregar.
   */
  protected function addRegla(Regla $regla)
  {
    $nombserv = $regla->getServicioId();
    if(isset($this->serv_reglas[$nombserv]))
      array_push($this->serv_reglas[$nombserv],$regla); 
    else
    {
      $this->serv_reglas[$nombserv] = array();
      array_push($this->serv_reglas[$nombserv],$regla);
    }
  }

  /**
   * devuelve el precio del paquete.
   */
  public function getPrecio()
  {
    return $this->precio;
  }

  /**
   * establece el precio del paquete.
   */
  public function setPrecio($precio)
  {
    $this->precio = $precio;
  }

  /*
  public function setNuevo()
  {
    $this->nuevo = true;
  }

  public function setAntiguo()
  {
    $this->nuevo = false;
  }
  

  public function getNuevo()
  {
    return $this->nuevo;
  }
  */

  public function esRepetido()
  {
    if(!strcmp($this->getStatus(),self::REPETIDO))
      return true;
    return false;
  }

  /**
   * Agrega un conjunto de reglas al paquete.
   *
   *@param $reglas Array<integer> Los id de las reglas.
   *
   *@throw sfException  Si alguna regla no concuerda 
   *                    con el producto o con las demas.
   */
  public function setReglas($idreglas)
  {
    $reglas = Doctrine_Core::getTable('Regla')->getReglas($idreglas);
    if( $this->reglasConcordes($reglas) )
      foreach($reglas as $regla)
      {
        if(!$regla->esDeInfluencia())
        {
          $factor = 1;
          if(-1 != $regla->getPosInfluencia())
            $factor = $reglas[$regla->getPosInfluencia()]->getCosto();

          $this->precio += $regla->getCosto(); 
        }

        $this->addRegla($regla);
      }
    else
      throw new sfException('las reglas no concuerdan');

  }
 
  /**
   * Devuelve el conjunto de reglas asociadas al paquete.
   *
   * @return array El conjunto de reglas incluidas en este paquete.
   */
  public function getReglas()
  {
    $result = array();
    foreach($this->serv_reglas as $nombre_serv => $reglas)
      foreach($reglas as $regla)
        array_push($result, $regla->toArray());
    return $result;

    //return $this->serv_reglas;
  }

  /**
   * Devulelve un arreglo con los id de las reglas que tiene este paquete.
   */
  public function getReglasIds()
  {
    $ids = array();
    foreach($this->serv_reglas as $nombserv => $reglas)
      foreach($reglas as $regla)
        array_push($ids, $regla->getPrimaryKey());

    return $ids;
  }

  public function getServReglas()
  {
    return $this->serv_reglas;
  }

  /**
   * Valida que las reglas esten acordes entre si y con el producto
   * en el paquete.
   *
   * @param $reglas array<Regla> Las reglas que se quieren validar.
   *
   * @return boolean             true en caso de ser validas, y false
   *                             en caso contrario.
   */
  protected function reglasConcordes($reglas = array())
  {
    $idprod = $this->producto->getPrimaryKey();
    foreach($reglas as $regla)
      if( !$this->concuerdReglaProducto($regla) || !$regla->concuerda($reglas) ) 
        return false;

    return true;
  }

  /**
   * Valida si la regla concuerda con el producto.
   *
   * @param  $regla Regla   La regla a verificar.
   * @return        boolean true en caso de que la regla sea valida para
   *                        este producto, y false en caso contrario.
   */
  protected function concuerdReglaProducto($regla)
  {
    if($regla->getProductoId() != $this->producto->getPrimaryKey())
      return false;
    return true;
  }

  /**
   * Remueve una regla de este paquete.
   *
   * @param $idregla integer  El id de la regla que se quiere remover.
   */
  public function removeRegla($idregla)
  {
    foreach($this->serv_reglas as $key => $regla)
      if($regla->getPrimaryKey() == $idregla)
      {
        unset($this->serv_reglas[$key]);  
        break;
      }
  }

  /**
   * Establece la frecuencia con que se cobra el paquete.
   *
   * @param $frec Frecuencia La frecuencia.
   */
  public function setFrecuencia(Frecuencia $frec)
  {
    $this->frecuencia = $frec;
  }

  /**
   * Devuelve la frecuencia de pagos de este paquete.
   * 
   * @return Frecuencia   La frecuencia.
   */
  public function getFrecuencia()
  {
    return $this->frecuencia;
  }

  /**
   * Devuelve una consulta de precio, tal cual se hubiera hecho para el producto 
   * empaquetado.
   * Un producto es empaquetado con una combinacion especifica de beneficios,
   * que son inducidos por la consulta que previamente hubiera hecho algun cliente.
   *
   *@return PricingRequest La consulta de precio del producto.
   */
  public function getConsulta()
  {
    $params = array();
    $params = $this->getAttReglas();
    $params['producto'] = $this->producto->getNombre();
    $params['frecuencia'] = $this->frecuencia->getPrimaryKey();
    $class = ucfirst(strtolower($this->producto->getNombre())) . 'PricingRequestForm';
    if(class_exists($class))
    {
      $presup = new $class();
      $presup->bind($params);
      return $presup;
    }
    
    throw new Exception('No existe la clase ' . $class);

  }

  /**
   * Devuelve los datos de las reglas del paquete en forma de atributos
   * para ser usados en una consulta.
   *
   * @return array array<string>  
   */
  public function getAttReglas()
  {
    $result = array();
    foreach($this->serv_reglas as $nombserv => $reglas)
      foreach($reglas as $count => $regla)
        $result['regla_' . $nombserv . '_' . $count] = $regla->getPrimaryKey();

    return $result;
  }

  /**
   * Devuelve el producto que estuviere empaquetado aqui.
   *
   * @return Producto El producto.
   */
  public function getProducto()
  {
    return $this->producto;
  }

  /**
   * Establece el producto empaquetado.
   *
   * @param $producto Producto El producto.
   */
  protected function setProducto(Producto $producto)
  {
    $this->producto = $producto;
  }

  /**
   * Despacha el paquete a su destino. En caso de ser
   * un producto instalable (programa), se instala para poder usarse.
   */
//  abstract public function despachar(InfoInstall $infoinstall);
//  TODO: PASAR LA IMPLEMENTACION DE ESTA CLASE A DEPACHADOR<NOMBRE_PROD>

  /**
   * Devuelve una orden de despacho del producto empaquetado.
   *
   * @param Cliente $cliente El cliente al que se dirige la orden.
   *
   * @param Pago    $pago    El pago para efectuar la orden.
   */
  public function getOrden(Cliente $cliente, Pago $pago) 
  {
    $entrega = new Entrega($this);
    $entrega->save();

    $nombprod = $this->getProducto()->getNombre();
    $clase = 'Instalar' . ucfirst(strtolower($nombprod));
    //if( class_exists($clase) )
      //return new $clase($entrega, $cliente, $pago);

    if(!class_exists($clase))
      throw new sfException('no existe la clase ' . $clase);

    $orden = new $clase();
    $orden->setEntrega($entrega);
    $orden->setCliente($cliente);
    $orden->setPago($pago);

    return $orden;

    
  }

  public function setEntrega(Entrega $entrega)
  {
    $this->entrega = $entrega;
  }

  public function getEntrega()
  {
    return $this->entrega;
  }
  
  public function setPlan(Plan $plan) {
      $this->plan = $plan;
  }

  public function getPlan() {
      return $this->plan;
  }
  
  

}
